#ifndef BASE_LOGGING_H
#define BASE_LOGGING_H

#include <cstdlib>
#include <iostream>
#include <string>

enum LogSeverity { INFO, WARNING, ERROR, FATAL, MUTE };

// The CHECK_xxxx facilities, which generates a segmentation fault
// when a check is failed.  If the program is run within a debugger,
// the segmentation fault makes the debugger keeps track of the stack,
// which provides the context of the fail.
//
extern char kSegmentFaultCauser[];

// The log facility, which makes it easy to leave of trace of your
// program.  The logs are classified according to their severity
// levels.  Logs of the level FATAL will cause a segmentation fault,
// which makes the debugger to keep track of the stack.
//
// Examples:
//   LOG(INFO) << iteration << "-th iteration ...";
//   LOG(FATAL) << "Probability value < 0 " << prob_value;
//

class Stream {
 public:
  Stream();
  Stream(int type);
  int type();
  void set_type(int type);
  ~Stream();
 private:
  static bool initialized_;
  int type_;
};

template <typename T>
inline Stream& operator<<(Stream& str, const T& v) {
  if (str.type() == INFO) {
    std::cout << v;
  } else if (str.type() != MUTE) {
    std::cerr << v;
  }
  return str;
}

class Logger {
 public:
  Logger(LogSeverity ls, const std::string& file, int line)
      : ls_(ls), file_(file), line_(line)
  {
    stream_.set_type(ls_);
  }
  Stream& stream() {
    return stream_ << file_.c_str() << " (" << line_ << ") : ";
  }
  ~Logger() {
    stream_ << "\n";
    /*
    if (ls_ == FATAL) {
      *::kSegmentFaultCauser = '\0';
      abort();
    }*/
  }
 private:
  Stream stream_;
  LogSeverity ls_;
  std::string file_;
  int line_;
};

#define LOG(ls) Logger(ls, __FILE__, __LINE__).stream()
namespace nsflags {
  extern int FLAGS_vlog;
}
#define VLOG(lv) Logger((nsflags::FLAGS_vlog) > (lv) ? MUTE : INFO, __FILE__, __LINE__).stream()

#define CONCATENATE2(x, y) x ## y
#define CONCATENATE(x, y) CONCATENATE2(x, y)

#define CHECK_OP(a, b, op) \
  bool CONCATENATE(c,__LINE__) = (!((a) op (b))); \
  if (CONCATENATE(c,__LINE__)) {     \
    std::cerr << "CHECK_EQ failed "                                     \
              << __FILE__ << ":" << __LINE__ << "\n"                    \
              << #a << #op << (a) << "\n"                             \
              << #b << #op << (b) << "\n";                            \
    *kSegmentFaultCauser = '\0';                                      \
  } Logger(!CONCATENATE(c,__LINE__) ? MUTE : FATAL, __FILE__, __LINE__).stream()

#define CHECK(a) CHECK_OP(a, true, ==)
#define CHECK_EQ(a, b) CHECK_OP(a, b, ==)
#define CHECK_GT(a, b) CHECK_OP(a, b, >)
#define CHECK_GE(a, b) CHECK_OP(a, b, >=)
#define CHECK_LT(a, b) CHECK_OP(a, b, <)
#define CHECK_LE(a, b) CHECK_OP(a, b, <=)

#endif
